package com.sam.wang.springframework.beans.factory.support;

import cn.hutool.core.bean.BeanException;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.sam.wang.springframework.beans.PropertyValue;
import com.sam.wang.springframework.beans.factory.config.BeanDefinition;
import com.sam.wang.springframework.beans.factory.config.BeanReference;
import com.sam.wang.springframework.core.io.Resource;
import com.sam.wang.springframework.core.io.ResourceLoader;
import com.sam.wang.springframework.beans.BeansException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.IOException;
import java.io.InputStream;

/**
 * @author wangchen
 * @version 1.0
 * @date 2021/9/9 11:03
 **/
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super((registry));
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeanException {
        try {
            try (InputStream is = resource.getInputStream()) {
                doLocationBeanDefinitions(is);
            }
        } catch (IOException | ClassNotFoundException e) {
            throw new BeansException("IOException parsing XML document from " + resource, e);
        }

    }

    private void doLocationBeanDefinitions(InputStream is) throws ClassNotFoundException{
        Document doc = XmlUtil.readXML(is);
        Element root = doc.getDocumentElement();
        NodeList childNodes = root.getChildNodes();
        for (int i = 0 ; i < childNodes.getLength(); i++) {
            if (!(childNodes.item(i) instanceof Element)) continue;;

            if (!"bean".equals(childNodes.item(i).getNodeName())) continue;

            // 解析标签
            Element bean = (Element) childNodes.item(i);
            String id = bean.getAttribute("id");
            String name = bean.getAttribute("name");
            String className = bean.getAttribute("class");

            // beanName 优先id
            Class<?> clazz = Class.forName(className);
            String beanName = StrUtil.isBlank(id)? name : id;
            // 如果没有配置取类名
            if (StrUtil.isEmpty(beanName)) {
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }

            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            // 读取属性值并填充
            for (int j = 0 ; j < bean.getChildNodes().getLength(); j++) {
                if (! (bean.getChildNodes().item(j) instanceof Element)) continue;
                if (! "property".equals(bean.getChildNodes().item(j).getNodeName())) continue;

                Element property = (Element) bean.getChildNodes().item(j);
                String attrName = property.getAttribute("name");
                String attrValue = property.getAttribute("value");
                String attrRef = property.getAttribute("ref");

                // 获取属性值, 引入对象,值对象
                Object value = StrUtil.isNotBlank(attrRef) ? new BeanReference(attrRef) : attrValue;
                // 创建属性信息
                PropertyValue propertyValue = new PropertyValue(attrName, attrValue);
                beanDefinition.getPropertyValues().addPropertyValue(propertyValue);

            }

            if (getBeanDefinitionRegistry().containsBeanDefinition(beanName)) {
                throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
            }

            // 注册beanDefinition
            getBeanDefinitionRegistry().registerBeanDefinition(beanName, beanDefinition);

        }

    }

    @Override
    public void loadBeanDefinitions(Resource... resources) throws BeansException {
        for (Resource resource : resources) {
            loadBeanDefinitions(resource);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }
}
